using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
using Debug = UnityEngine.Debug;

// ReSharper disable once CheckNamespace
public class ShaderComplexity : EditorWindow
{
    private const string DEST_DIRECTORY_PATH = "Assets/TempShaderCompiled";
    
    private static ShaderComplexity m_Instance = null;
    private string m_CompiledShaderPath;
    private string m_OCResult = string.Empty;
    private List<ShaderInfo> m_ShaderInfos = new List<ShaderInfo>();
    private string m_Architecture = "Mali-T720";

    private static string s_ProjectDirectory = string.Empty;
    private Vector2 m_ScrollPos = Vector2.zero;
    private int m_ShowMode = 0;
    private string m_SearchPattern;
    List<string> m_SearchPatterns = new List<string>();
    private static string[] m_ArchitectureList = new[] {
        // Arm 5th Generation architecture
        "Immortalis-G720", "Mali-G720", "Mali-G620",
        // Valhall architecture
        "Mali-G715", "Mali-G710", "Mali-G615", "Mali-G610", "Mali-G510", "Mali-G310", "Mali-G78AE", "Mali-G78", "Mali-G77", "Mali-G68", "Mali-G57",
        // Bifrost architecture
        "Mali-G76", "Mali-G72", "Mali-G71", "Mali-G52", "Mali-G51", "Mali-G31",
        // Midgard architecture
        "Mali-T880", "Mali-T860", "Mali-T830", "Mali-T820", "Mali-T760", "Mali-T720"
    };
    private enum ShaderSourceType
    {
        GLSL,
        GameObject,
        Material,
    }
    private ShaderSourceType m_ShaderSourceType = ShaderSourceType.GLSL;
        
    private GameObject m_SelectedGameObject;
    private List<Material> m_MaterialsOnGameObject = new List<Material>();

    private Material m_SelectedMaterial;
    private bool m_IncludeAllVariants;

    private CompileGLSLRequest m_CompiledRequest;
    
    [MenuItem("Tools/Shader Complexity")]
    private static void Open()
    {
        if (m_Instance != null)
        {
            m_Instance.Close();
            m_Instance = null;
        }

        m_Instance = GetWindow<ShaderComplexity>();
        m_Instance.titleContent = new GUIContent("Shader复杂度计算器");
        m_Instance.minSize = new Vector2(550.0f, 600.0f);
        m_Instance.Show();
    }

    private void OnEnable()
    {
        m_CompiledRequest = null;
        int assetIndex = Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal);
        s_ProjectDirectory = Application.dataPath.Substring(0, assetIndex).Replace('\\', '/');
        
        // DirectoryInfo destDir = new DirectoryInfo(DEST_DIRECTORY_PATH);
        // if (destDir.Exists)
        // {
        //     destDir.Delete(true);
        // }
        // if (File.Exists(DEST_DIRECTORY_PATH + ".meta"))
        // {
        //     File.Delete(DEST_DIRECTORY_PATH + ".meta");
        // }
    }
    
    private void OnDestroy()
    {
        DirectoryInfo destDir = new DirectoryInfo(DEST_DIRECTORY_PATH);
        if (destDir.Exists)
        {
            if (EditorUtility.DisplayDialog("提示", "是否清空所有临时文件？", "清空", "取消"))
            {
                destDir.Delete(true);
                if (File.Exists(DEST_DIRECTORY_PATH + ".meta"))
                {
                    File.Delete(DEST_DIRECTORY_PATH + ".meta");
                }
            }
        }
    }
    
    private void OnGUI()
    {
        EditorGUILayout.BeginVertical("TextArea");
        EditorGUILayout.Space();
        string[] shaderSrcTexts = new[]
        {
            "编译后的GLES 3.0 Shader",
            "指定GameObject",
            "指定材质"
        };
        m_ShaderSourceType = (ShaderSourceType) GUILayout.Toolbar((int) m_ShaderSourceType, shaderSrcTexts);
        switch (m_ShaderSourceType)
        {
            case ShaderSourceType.GLSL:
            {
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.TextField("编译后的GLES 3.0 Shader", m_CompiledShaderPath);
                if (GUILayout.Button("打开", GUILayout.Width(44.0f)))
                {
                    string path = EditorUtility.OpenFilePanel("打开", s_ProjectDirectory, "txt");
                    if (!string.IsNullOrEmpty(path))
                    {
                        m_CompiledShaderPath = path;
                    }
                }

                EditorGUILayout.EndHorizontal();
                break;
            }
            case ShaderSourceType.GameObject:
                {
                    GameObject newSelectd = EditorGUILayout.ObjectField("指定GameObject", m_SelectedGameObject, typeof(GameObject), true) as GameObject;
                    if (newSelectd != m_SelectedGameObject)
                    {
                        m_MaterialsOnGameObject.Clear();
                        m_SelectedGameObject = newSelectd;
                        if (m_SelectedGameObject != null)
                        {
                            if (PrefabUtility.IsPartOfPrefabAsset(m_SelectedGameObject) &&
                                EditorUtility.DisplayDialog("提示", "检测到选中GameObject为Prefab资源，是否需要实例化出来？", "确定", "取消"))
                            {
                                m_SelectedGameObject = PrefabUtility.InstantiatePrefab(m_SelectedGameObject) as GameObject;
                            }
                            if (m_SelectedGameObject == null)
                            {
                                break;
                            }
                            Renderer r = m_SelectedGameObject.GetComponent<Renderer>();
                            if (r == null)
                            {
                                break;
                            }
                            m_MaterialsOnGameObject.AddRange(r.sharedMaterials);
                        }
                    }
                    EditorGUILayout.BeginHorizontal();
                    GUILayout.Space(16.0f);
                    float origWidth = EditorGUIUtility.labelWidth;
                    EditorGUIUtility.labelWidth = 48.0f;
                    EditorGUILayout.BeginVertical();
                    for (int i = 0; i < m_MaterialsOnGameObject.Count; ++i)
                    {
                        Material mat = m_MaterialsOnGameObject[i];
                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.ObjectField($"材质[{i}]", mat, typeof(Material), true);
                        if (GUILayout.Button("选中", GUILayout.Width(44.0f)))
                        {
                            m_SelectedMaterial = mat;
                            m_ShaderSourceType = ShaderSourceType.Material;
                        }
                        EditorGUILayout.EndHorizontal();
                    }
                    EditorGUILayout.EndVertical();
                    EditorGUIUtility.labelWidth = origWidth;
                    EditorGUILayout.EndHorizontal();
                    break;
                }
                case ShaderSourceType.Material:
                {
                    m_SelectedMaterial = EditorGUILayout.ObjectField("材质", m_SelectedMaterial, typeof(Material), true) as Material;
                    m_IncludeAllVariants = EditorGUILayout.Toggle("是否包含所有变体", m_IncludeAllVariants);
                    if (m_SelectedMaterial && GUILayout.Button("编译Shader", GUILayout.Height(32.0f)))
                    {
                        if (!Directory.Exists(DEST_DIRECTORY_PATH))
                        {
                            Directory.CreateDirectory(DEST_DIRECTORY_PATH);
                        }
                        m_CompiledRequest = RequestCompile(m_SelectedMaterial.shader, m_IncludeAllVariants, DEST_DIRECTORY_PATH);
                        string keywords = string.Empty;
                        foreach (string keyword in m_SelectedMaterial.shaderKeywords)
                        {
                            keywords += keyword + " ";
                        }
                        m_SearchPattern = keywords;
                    }
                    break;
                }
            }
            EditorGUILayout.Space();
            EditorGUILayout.EndVertical();

        int selectIndex = 0;
        for (int i = 0; i < m_ArchitectureList.Length; ++i)
        {
            string arch = m_ArchitectureList[i];
            if (arch == m_Architecture)
            {
                selectIndex = i;
                break;
            }
        }
        
        selectIndex = EditorGUILayout.Popup("GPU架构", selectIndex, m_ArchitectureList);
        m_Architecture = m_ArchitectureList[selectIndex];
        
        if (!string.IsNullOrEmpty(m_CompiledShaderPath) && GUILayout.Button("分析", GUILayout.Height(32.0f)))
        {
            m_OCResult = string.Empty;
            m_ShaderInfos.Clear();
            // m_SearchPattern = string.Empty;
            m_SearchPatterns.Clear();
            ShaderDisassemble();
        }
        
        m_ShowMode = GUILayout.Toolbar(m_ShowMode, new [] { new GUIContent("列表模式"), new GUIContent("文本模式") });
        if (m_ShowMode == 0)
        {
            ShowUIMode();
        }
        else if (m_ShowMode == 1)
        {
            ShowTextMode();
        }
        
        if (m_CompiledRequest != null)
        {
            if (m_CompiledRequest.m_IsDone)
            {
                if (string.IsNullOrEmpty(m_CompiledRequest.m_Error))
                {
                    m_CompiledShaderPath = m_CompiledRequest.m_ShaderPath;
                    m_ShaderSourceType = ShaderSourceType.GLSL;
                }
                m_CompiledRequest = null;
            }
            Repaint();
        }
    }
    
    #region 请求编译GLSL文件
    
    public class CompileGLSLRequest
    {
        public bool m_IsDone;
        public string m_Error;
        public string m_TempPath;
        public string m_ShaderPath;

        private bool m_FileReady;

        // ReSharper disable once ConvertConstructorToMemberInitializers
        public CompileGLSLRequest()
        {
            m_IsDone = false;
            m_Error = null;
            m_TempPath = string.Empty;
            m_ShaderPath = string.Empty;
            m_FileReady = false;
        }
        
        internal void OnLoading()
        {
            try
            {
                if (m_FileReady)
                {
                    File.Copy(m_TempPath, m_ShaderPath, true);
                    m_IsDone = true;
                    EditorApplication.update -= this.OnLoading;
                    return;
                }
                FileInfo fileInfo = new FileInfo(m_TempPath);
                if (!fileInfo.Exists)
                {
                    return;
                }
                FileStream fileStream = fileInfo.Open(FileMode.Open);
                fileStream.Close();

                m_FileReady = true;
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                m_IsDone = true;
                m_Error = e.ToString();
                EditorApplication.update -= this.OnLoading;
            }
        }
    }

    public static CompileGLSLRequest RequestCompile(Shader shader, bool includeAllVariants, string destDirectoryPath)
    {
        string projectPath = Application.dataPath.Replace("Assets", "");
        string tempPath = projectPath + "Temp/";
        string tempName = "Compiled-" + shader.name.Replace("/", "-") + ".shader";
        string compiledFilePath = tempPath + tempName;
        if (File.Exists(compiledFilePath))
        {
            File.Delete(compiledFilePath);
        }
        
        CompileGLSLRequest req = new CompileGLSLRequest();

        MethodInfo openCompiledShaderMethod = typeof(ShaderUtil).GetMethod("OpenCompiledShader", BindingFlags.Static | BindingFlags.NonPublic);
        if (openCompiledShaderMethod == null)
        {
            req.m_IsDone = true;
            req.m_Error = $"Fail to get method: ShaderUtil.OpenCompiledShader";
            return req;
        }

        openCompiledShaderMethod.Invoke(null, new object[] {shader,
            3 /* Custom: */,
            1 << (int)ShaderCompilerPlatform.GLES3x,
            includeAllVariants,
            /*preprocessOnly*/false,
            /*stripLineDirectives*/false});

        req.m_TempPath = compiledFilePath;
        req.m_ShaderPath = Path.Combine(destDirectoryPath, Path.GetFileNameWithoutExtension(tempName) + ".txt").Replace("\\", "/");
        EditorApplication.update += req.OnLoading;
        
        return req;
    }
    
    #endregion

    private void ShowTextMode()
    {
        m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos);
        
        EditorGUILayout.TextArea(m_OCResult);
        
        EditorGUILayout.EndScrollView();

        if (GUILayout.Button("保存"))
        {
            string path = EditorUtility.SaveFilePanel("保存", string.Empty, "Analysis", "txt");
            if (!string.IsNullOrEmpty(path))
            {
                File.WriteAllText(path, m_OCResult);
                AssetDatabase.Refresh();
            }
        }
    }

    private void ShowUIMode()
    {
        EditorGUILayout.BeginHorizontal();
        m_SearchPattern = EditorGUILayout.TextField("搜索Keyword", m_SearchPattern);
        if (GUILayout.Button("搜索", GUILayout.Width(44.0f)))
        {
            m_SearchPatterns.Clear();
            string[] patterns = m_SearchPattern.Split(new[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);
            m_SearchPatterns.AddRange(patterns);
        }
        EditorGUILayout.EndHorizontal();
        
        m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos);

        int searchCount = 0;
        for (int i = 0; i < m_ShaderInfos.Count; ++i)
        {
            ShaderInfo shaderInfo = m_ShaderInfos[i];
            
            // 根据搜索框的内容，搜索Keyword
            if (m_SearchPatterns.Count > 0)
            {
                int foundCount = 0;
                for (int j = 0; j < m_SearchPatterns.Count; ++j)
                {
                    string p = m_SearchPatterns[j];
                    if (shaderInfo.m_LocalKeywords.Contains(p))
                    {
                        ++foundCount;
                    }
                    else if (shaderInfo.m_GlobalKeywords.Contains(p))
                    {
                        ++foundCount;
                    }
                }
                if (foundCount < m_SearchPatterns.Count)
                {
                    continue;
                }
            }
            ++searchCount;

            EditorGUILayout.BeginVertical("Button");
            shaderInfo.Expand = EditorGUILayout.Foldout(shaderInfo.Expand, "Shader变体：" + shaderInfo.m_ShaderName);
            if (!shaderInfo.Expand)
            {
                EditorGUILayout.EndVertical();
                continue;
            }
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(22.0f);
            EditorGUILayout.BeginVertical();
            
            EditorGUILayout.BeginVertical("TextArea");

            EditorGUILayout.LabelField("类型", shaderInfo.m_ShaderType);

            #region 全局关键字

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("全局关键字", GUILayout.Width(120.0f));
            EditorGUILayout.TextArea(shaderInfo.m_GlobalKeywordsText);
            EditorGUILayout.EndHorizontal();

            #endregion
            
            #region 局部关键字
            
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("局部关键字", GUILayout.Width(120.0f));
            EditorGUILayout.TextArea(shaderInfo.m_LocalKeywordsText);
            EditorGUILayout.EndHorizontal();

            #endregion
            
            if (shaderInfo.m_DefaultInfo != null)
            {
                OnShaderBaseInfoGUI(shaderInfo.m_DefaultInfo, "默认变体");
            }
            if (shaderInfo.m_PositionVariant != null)
            {
                OnShaderBaseInfoGUI(shaderInfo.m_PositionVariant, "位置变体(Position Variant)");
            }
            if (shaderInfo.m_VaringVariant != null)
            {
                OnShaderBaseInfoGUI(shaderInfo.m_VaringVariant, "插值变体(Varing Variant)");
            }

            if (shaderInfo.m_ShaderProperties != null)
            {
                OnShaderPropertiesGUI(shaderInfo.m_ShaderProperties);
            }
            
            EditorGUILayout.EndVertical();
            
            EditorGUILayout.EndVertical();
            GUILayout.Space(22.0f);
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.Space();
        }
        EditorGUILayout.EndScrollView();

        if (m_SearchPatterns.Count > 0)
        {
            EditorGUILayout.LabelField("搜索结果", string.Format("{0} 个变体", searchCount));
        }

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("折叠所有"))
        {
            for (int i = 0; i < m_ShaderInfos.Count; ++i)
            {
                ShaderInfo shaderInfo = m_ShaderInfos[i];
                shaderInfo.Expand = false;
            }
        }
        if (GUILayout.Button("展开所有"))
        {
            for (int i = 0; i < m_ShaderInfos.Count; ++i)
            {
                ShaderInfo shaderInfo = m_ShaderInfos[i];
                shaderInfo.Expand = true;
            }
        }
        EditorGUILayout.EndHorizontal();
    }

    private void OnShaderPropertiesGUI(ShaderProperties properties)
    {
        EditorGUILayout.LabelField("Shader属性：");
        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(22.0f);
        EditorGUILayout.BeginVertical();
        {
            string tips;
            tips = "这是一种仅依赖字面量或者Uniform值的计算，因此在每一个线程都会产生相同的结果。虽然驱动程序可以优化这一点，但它仍然有成本，所以尽可能把它从着色器移到CPU逻辑中。uniform应该为常量。";
            EditorGUILayout.TextField(new GUIContent("是否有Uniform计算", tips), properties.m_HasUniformComputation.ToString());
            
            tips = "在固定图形管道之外，显示这个着色器是否有在内存中可见的副作用。它们可能由以下原因引起：\r\n" +
                   "● 写入着色器存储缓冲区（storage buffer）\r\n" +
                   "● 存储到图像中\r\n" +
                   "● 原子操作（atomics）的使用\r\n" +
                   "有副作用的着色器不能通过诸如隐藏表面消除（hidden surface removal, HSR）之类的技术来优化，所以它们的使用应该最小化。";
            EditorGUILayout.TextField(new GUIContent("是否有副作用", tips), properties.m_HasSideEffects.ToString());
            
            tips = "判断是否片段着色器包含覆盖掩码，可以通过着色器的执行来改变。例如discard操作。具有可修改覆盖严码的Shader，肯定会使用Late ZS Update，这降低了同一像素进行Early ZS Test的效率。";
            EditorGUILayout.TextField(new GUIContent("是否修改覆盖", tips), properties.m_ModifiesCoverage.ToString());
            
            tips = "片段着色器是否包含Late ZS Test操作，例如写了gl_FragDepth。Late ZS负责处理那些像素着色之前无法处理的深度、模板测试以及更新。";
            EditorGUILayout.TextField(new GUIContent("是否使用了Late ZS Test", tips), properties.m_UsesLateZSTest.ToString());
            
            tips = "片段着色器是否包含Late ZS Update逻辑，例如，通过使用gl_LastFragDepthARM读取着色器中的旧深度值。";
            EditorGUILayout.TextField(new GUIContent("是否使用了Late ZS Update", tips), properties.m_UseLateZSUpdate.ToString());
            
            tips = "片段着色器是否包含从颜色缓冲区（color buffer）读取的逻辑，例如，从gl_LastFragColorARM里读取。以这种方式读取的Shader，被视为透明的，不能用作隐藏表面消除（hidden surface removal, HSR）器。";
            EditorGUILayout.TextField(new GUIContent("是否读取颜色缓冲区", tips), properties.m_ReadColorBuffer.ToString());
        }
        EditorGUILayout.EndVertical();
        EditorGUILayout.EndHorizontal();
    }

    private void OnShaderBaseInfoGUI(ShaderBaseInfo baseInfo, string variantName)
    {
        EditorGUILayout.LabelField(variantName + "：");
        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(22.0f);
        EditorGUILayout.BeginVertical();
        {
            EditorGUILayout.IntField("工作寄存器", baseInfo.m_WorkRegisters);
            EditorGUILayout.IntField("Uniform寄存器", baseInfo.m_UniformRegisters);
            EditorGUILayout.Toggle("是否有变量被放置到栈内存中", baseInfo.m_StackSpilling);
            EditorGUILayout.TextField("16位运算", $"{baseInfo.m_Arithmetic16Bit * 1e2f}%");
            
            EditorGUILayout.LabelField("Shader复杂度：");
            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(22.0f);
            EditorGUILayout.BeginVertical();
            {
                float valueWidth = 50.0f;
                
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField(" ", GUILayout.Width(160.0f));
                EditorGUILayout.LabelField("A", GUILayout.Width(valueWidth));
                EditorGUILayout.LabelField("LS", GUILayout.Width(valueWidth));
                EditorGUILayout.LabelField("V", GUILayout.Width(valueWidth));
                EditorGUILayout.LabelField("T", GUILayout.Width(valueWidth));
                EditorGUILayout.LabelField("瓶颈", GUILayout.Width(valueWidth));
                EditorGUILayout.EndHorizontal();
                
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("指令的累积执行Cycles", GUILayout.Width(160.0f));
                EditorGUILayout.FloatField(baseInfo.m_TotalShaderCycles.m_A, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_TotalShaderCycles.m_LS, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_TotalShaderCycles.m_V, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_TotalShaderCycles.m_T, GUILayout.Width(valueWidth));
                EditorGUILayout.TextField(baseInfo.m_TotalShaderCycles.m_Bound, GUILayout.Width(valueWidth));
                EditorGUILayout.EndHorizontal();
            
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("最短控制流路径Cycles", GUILayout.Width(160.0f));
                EditorGUILayout.FloatField(baseInfo.m_ShortestShaderCycles.m_A, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_ShortestShaderCycles.m_LS, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_ShortestShaderCycles.m_V, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_ShortestShaderCycles.m_T, GUILayout.Width(valueWidth));
                EditorGUILayout.TextField(baseInfo.m_ShortestShaderCycles.m_Bound, GUILayout.Width(valueWidth));
                EditorGUILayout.EndHorizontal();
            
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("最长控制流路径Cycles", GUILayout.Width(160.0f));
                EditorGUILayout.FloatField(baseInfo.m_LongestShaderCycles.m_A, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_LongestShaderCycles.m_LS, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_LongestShaderCycles.m_V, GUILayout.Width(valueWidth));
                EditorGUILayout.FloatField(baseInfo.m_LongestShaderCycles.m_T, GUILayout.Width(valueWidth));
                EditorGUILayout.TextField(baseInfo.m_LongestShaderCycles.m_Bound, GUILayout.Width(valueWidth));
                EditorGUILayout.EndHorizontal();
            }
            EditorGUILayout.EndVertical();
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndVertical();
        EditorGUILayout.EndHorizontal();
    }
    
    /// <summary>
    /// 通过malioc来解析Shader变体
    /// </summary>
    /// <param name="path">编译成GLES 3.X的Shader变体文件路径</param>
    /// <param name="shaderName">Shader变体的名称</param>
    /// <param name="shaderType">Shader变体的类型（VS/PS）</param>
    /// <param name="globalKW">全局关键字</param>
    /// <param name="localKW">局部关键字</param>
    void MaliOC(string path, string shaderName, string shaderType, string globalKW, string localKW)
    {
        string maliocPath = s_ProjectDirectory + "Tools/mali_offline_compiler/malioc.exe";

        string command = $"\"{maliocPath}\" -c {m_Architecture} \"{path}\"";
        
        ProcessStartInfo startInfo = new ProcessStartInfo()
        {
            FileName = "cmd.exe",
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            UseShellExecute = false,
            CreateNoWindow = true
        };
        Process process = new Process();
        process.StartInfo = startInfo;
        process.Start();
        
        process.StandardInput.WriteLine(command + "&exit");
        process.StandardInput.AutoFlush = true;
        
        string output = process.StandardOutput.ReadToEnd();
        string error = process.StandardError.ReadToEnd();
        
        m_OCResult += "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■\r\n";
        m_OCResult += globalKW + "\r\n";
        m_OCResult += localKW + "\r\n";
        m_OCResult += "Shader名称：" + shaderName + "\r\n";
        m_OCResult += "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■\r\n";
        if (!string.IsNullOrEmpty(output))
        {
            ParseShaderInfo(output, shaderName, shaderType, globalKW, localKW);
            
            output = output.Replace("Work registers:", "工作寄存器：");
            output = output.Replace("Uniform registers:", "Uniform寄存器：");
            output = output.Replace("Stack spilling:", "是否有变量被放置到栈内存中：");
            output = output.Replace("Total instruction cycles:", "指令的累积执行Cycles：    ");
            output = output.Replace("Shortest path cycles:", "最短控制流路径Cycles：");
            output = output.Replace("Longest path cycles:", "最长控制流路径Cycles：");
        }
        m_OCResult += output + "\r\n";
        if (!string.IsNullOrEmpty(error))
        {
            m_OCResult += "Error:\r\n" + error + "\r\n";
        }
        process.WaitForExit();
        process.Close();
    }
    
    /// <summary>
    /// 将编译成GLES 3.X的Shader拆分出变体，并分析每一个变体
    /// </summary>
    private void ShaderDisassemble()
    {
        string text = File.ReadAllText(m_CompiledShaderPath);

        int curIndex = 0;

        string vertexBeginFlag = "#ifdef VERTEX";
        string fragBeginFlag = "#ifdef FRAGMENT";
        string endFlag = "#endif";
        int shaderIndex = 1;

        string path = m_CompiledShaderPath.Replace("\\", "/");
        string fileName = Path.GetFileNameWithoutExtension(path);
        path = path.Substring(0, path.LastIndexOf("/", StringComparison.Ordinal));

        m_OCResult += "GPU Architecture: " + m_Architecture + "\r\n\r\n";

        string keywordFlag = "Global Keywords:";
        if (!text.Contains(keywordFlag))
        {
            keywordFlag = "Keywords:";
            text = text.Replace("300 es", "310 es");
        }

        while ((curIndex = text.IndexOf(keywordFlag, curIndex, StringComparison.Ordinal)) >= 0)
        {
            int globalKWEnd = text.IndexOf("\n", curIndex, StringComparison.Ordinal);
            string globalKW = text.Substring(curIndex, globalKWEnd - curIndex);

            string localKW = string.Empty;
            if (keywordFlag == "Global Keywords:")
            {
                // Build-In管线的Shader
                int localKWBegin = text.IndexOf("Local Keywords:", curIndex, StringComparison.Ordinal);
                int localKWEnd = text.IndexOf("\n", localKWBegin, StringComparison.Ordinal);
                localKW = text.Substring(localKWBegin, localKWEnd - localKWBegin);
            }
            
            int vertexBeginIndex = text.IndexOf(vertexBeginFlag, curIndex, StringComparison.Ordinal) + vertexBeginFlag.Length;
            int fragBeginIndex = text.IndexOf(fragBeginFlag, curIndex, StringComparison.Ordinal) + fragBeginFlag.Length;
            int nextIndex = text.IndexOf(keywordFlag, fragBeginIndex, StringComparison.Ordinal);

            if (vertexBeginIndex < fragBeginIndex)
            {
                int vertexEndIndex = text.LastIndexOf(endFlag, fragBeginIndex, StringComparison.Ordinal);
                string vertexShader = text.Substring(vertexBeginIndex + 1, vertexEndIndex - vertexBeginIndex - 1);
                vertexShader = vertexShader + "\n// " + globalKW;
                vertexShader = vertexShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref vertexShader);
                string shaderName1 = fileName + "_" + shaderIndex + ".vert";
                string vertPath = Path.Combine(path, shaderName1).Replace("\\", "/");
                File.WriteAllText(vertPath, vertexShader);
                
                AssetDatabase.Refresh();
                MaliOC(vertPath, shaderName1, "VertexShader", globalKW, localKW);

                int fragEndIndex = nextIndex < 0 ? text.LastIndexOf(endFlag, text.Length - 1, StringComparison.Ordinal) : text.LastIndexOf(endFlag, nextIndex, StringComparison.Ordinal);
                string fragShader = text.Substring(fragBeginIndex + 1, fragEndIndex - fragBeginIndex - 1);
                fragShader = fragShader + "\n// " + globalKW;
                fragShader = fragShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref fragShader);
                string shaderName2 = fileName + "_" + shaderIndex + ".frag";
                string fragPath = Path.Combine(path, shaderName2).Replace("\\", "/");
                File.WriteAllText(fragPath, fragShader);
                
                AssetDatabase.Refresh();
                MaliOC(fragPath, shaderName2, "FragmentShader", globalKW, localKW);
            }
            else
            {
                int fragEndIndex = text.LastIndexOf(endFlag, vertexBeginIndex, StringComparison.Ordinal);
                string fragShader = text.Substring(fragBeginIndex + 1, fragEndIndex - fragEndIndex - 1);
                fragShader = fragShader + "\n// " + globalKW;
                fragShader = fragShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref fragShader);
                string shaderName1 = fileName + "_" + shaderIndex + ".frag";
                string fragPath = Path.Combine(path, shaderName1).Replace("\\", "/");
                File.WriteAllText(fragPath, fragShader);
                
                AssetDatabase.Refresh();
                MaliOC(fragPath, shaderName1, "FragmentShader", globalKW, localKW);

                int vertEndIndex = nextIndex < 0 ? text.LastIndexOf(endFlag, text.Length - 1, StringComparison.Ordinal) : text.LastIndexOf(endFlag, nextIndex, StringComparison.Ordinal);
                string vertShader = text.Substring(vertexBeginIndex + 1, vertEndIndex - vertexBeginIndex - 1);
                vertShader = vertShader + "\n// " + globalKW;
                vertShader = vertShader + "\n// " + localKW;
                GuaranteeVersionIsFirstStat(ref vertShader);
                string shaderName2 = fileName + "_" + shaderIndex + ".vert";
                string vertPath = Path.Combine(path, shaderName2).Replace("\\", "/");
                File.WriteAllText(vertPath, vertShader);
                
                AssetDatabase.Refresh();
                MaliOC(vertPath, shaderName2, "VertexShader", globalKW, localKW);
            }
			
			curIndex = fragBeginIndex;
			if (shaderIndex > 100)
			{
				break;
			}

            ++shaderIndex;
        }
    }
    
    /// <summary>
    /// 保证#version是第一句话
    /// </summary>
    /// <param name="shaderText">Shader文本</param>
    void GuaranteeVersionIsFirstStat(ref string shaderText)
    {
        int versionIndex = shaderText.IndexOf("#version", StringComparison.Ordinal);
        if (versionIndex < 0)
        {
            return;
        }
        shaderText = shaderText.Substring(versionIndex);
    }
    
    /// <summary>
    /// 当前读取解析的状态
    /// </summary>
    private enum ReadState
    {
        None,
        MainShader,
        DefaultVariant,
        PositionVariant,
        VaryingVariant,
        ShaderProperties,
    }
    
    /// <summary>
    /// 根据Shader文本解析ShaderInfo数据，用于在编辑器里显示
    /// </summary>
    /// <param name="ocResult">Shader文本</param>
    /// <param name="shaderName">Shader变体名称</param>
    /// <param name="shaderType">Shader类型（VS/PS）</param>
    /// <param name="globalKeyword">全局关键字</param>
    /// <param name="localKeyword">局部关键字</param>
    private void ParseShaderInfo(string ocResult, string shaderName, string shaderType, string globalKeyword, string localKeyword)
    {
        ShaderInfo shaderInfo = new ShaderInfo();
        shaderInfo.m_ShaderName = shaderName;
        shaderInfo.m_ShaderType = shaderType;

        string[] gkwDatas = globalKeyword.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
        if (gkwDatas.Length >= 2)
        {
            string[] gkws = gkwDatas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            shaderInfo.m_GlobalKeywords.AddRange(gkws);
            
            bool first = true;
            foreach (string keyword in shaderInfo.m_GlobalKeywords)
            {
                if (!first)
                {
                    shaderInfo.m_GlobalKeywordsText += ", ";
                }
                shaderInfo.m_GlobalKeywordsText += keyword;
                first = false;
            }
        }
        string[] lkwDatas = localKeyword.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
        if (lkwDatas.Length >= 2)
        {
            string[] lkws = lkwDatas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
            shaderInfo.m_LocalKeywords.AddRange(lkws);
            
            bool first = true;
            foreach (string keyword in shaderInfo.m_LocalKeywords)
            {
                if (!first)
                {
                    shaderInfo.m_LocalKeywordsText += ", ";
                }
                shaderInfo.m_LocalKeywordsText += keyword;
                first = false;
            }
        }

        ReadState readState = ReadState.None;
        
        int pvIndex = ocResult.IndexOf("Position variant", StringComparison.Ordinal);
        int vvIndex = ocResult.IndexOf("Varying variant", StringComparison.Ordinal);

        ShaderBaseInfo currentBaseInfo = null;
        List<string> currentCyclesFormat = new List<string>();

        using (StringReader reader = new StringReader(ocResult))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                switch (line)
                {
                    case "Main shader":
                    {
                        readState = ReadState.MainShader;
                        if (pvIndex < 0 && vvIndex < 0)
                        {
                            readState = ReadState.DefaultVariant;
                            currentBaseInfo = new ShaderBaseInfo();
                            shaderInfo.m_DefaultInfo = currentBaseInfo;
                        }
                        break;
                    }
                    case "Position variant":
                    {
                        readState = ReadState.PositionVariant;
                        currentBaseInfo = new ShaderBaseInfo();
                        shaderInfo.m_PositionVariant = currentBaseInfo;
                        break;
                    }
                    case "Varying variant":
                    {
                        readState = ReadState.VaryingVariant;
                        currentBaseInfo = new ShaderBaseInfo();
                        shaderInfo.m_VaringVariant = currentBaseInfo;
                        break;
                    }
                    case "Shader properties":
                    {
                        readState = ReadState.ShaderProperties;
                        shaderInfo.m_ShaderProperties = new ShaderProperties();
                        break;
                    }
                }
                
                switch (readState)
                {
                    case ReadState.DefaultVariant:
                    case ReadState.PositionVariant:
                    case ReadState.VaryingVariant:
                    {
                        if (currentBaseInfo == null)
                        {
                            break;
                        }
                        string[] datas = line.Split(new []{ ':' }, StringSplitOptions.RemoveEmptyEntries);
                        if (datas.Length <= 0)
                        {
                            break;
                        }
                        string key = datas[0];
                        switch (key)
                        {
                            case "Work registers":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                int.TryParse(innerDatas[0], out currentBaseInfo.m_WorkRegisters);
                                break;
                            }
                            case "Uniform registers":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                int.TryParse(innerDatas[0], out currentBaseInfo.m_UniformRegisters);
                                break;
                            }
                            case "Stack spilling":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                bool.TryParse(innerDatas[0], out currentBaseInfo.m_StackSpilling);
                                break;
                            }
                            case "16-bit arithmetic":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                string num = innerDatas[0];
                                num = num.Replace("%", "");
                                if (float.TryParse(num, out float n))
                                {
                                    currentBaseInfo.m_Arithmetic16Bit = n * 1e-2f;
                                }
                                break;
                            }
                            case "Total instruction cycles":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                ParseCycles(currentCyclesFormat, currentBaseInfo.m_TotalShaderCycles, innerDatas);
                                break;
                            }
                            case "Shortest path cycles":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                ParseCycles(currentCyclesFormat, currentBaseInfo.m_ShortestShaderCycles, innerDatas);
                                break;
                            }
                            case "Longest path cycles":
                            {
                                string[] innerDatas = datas[1].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                                ParseCycles(currentCyclesFormat, currentBaseInfo.m_LongestShaderCycles, innerDatas);
                                break;
                            }
                        }
                        if (key.Contains("A "))
                        {
                            currentCyclesFormat.Clear();
                            string[] innerDatas = key.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
                            currentCyclesFormat.AddRange(innerDatas);
                        }
                        break;
                    }
                    case ReadState.ShaderProperties:
                    {
                        if (shaderInfo.m_ShaderProperties == null)
                        {
                            break;
                        }
                        string[] datas = line.Split(new []{ ':' }, StringSplitOptions.RemoveEmptyEntries);
                        if (datas.Length <= 0)
                        {
                            break;
                        }
                        string key = datas[0];
                        switch (key)
                        {
                            case "Has uniform computation":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_HasUniformComputation);
                                break;
                            }
                            case "Has side-effects":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_HasSideEffects);
                                break;
                            }
                            case "Modifies coverage":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_ModifiesCoverage);
                                break;
                            }
                            case "Uses late ZS test":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_UsesLateZSTest);
                                break;
                            }
                            case "Uses late ZS update":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_UseLateZSUpdate);
                                break;
                            }
                            case "Reads color buffer":
                            {
                                bool.TryParse(datas[1], out shaderInfo.m_ShaderProperties.m_ReadColorBuffer);
                                break;
                            }
                        }

                        break;
                    }
                }
            }
        }
        
        m_ShaderInfos.Add(shaderInfo);
    }

    private void ParseCycles(List<string> currentCyclesFormat, ShaderBaseInfo.ShaderCycles shaderCycles, string[] datas)
    {
        for (int i = 0; i < currentCyclesFormat.Count; ++i)
        {
            string format = currentCyclesFormat[i];
            string data = datas[i];
            if (i < currentCyclesFormat.Count - 1)
            {
                switch (format)
                {
                    case "A":
                        float.TryParse(data, out shaderCycles.m_A);
                        break;
                    case "LS":
                        float.TryParse(data, out shaderCycles.m_LS);
                        break;
                    case "V":
                        float.TryParse(data, out shaderCycles.m_V);
                        break;
                    case "T":
                        float.TryParse(data, out shaderCycles.m_T);
                        break;
                }
            }
            else
            {
                shaderCycles.m_Bound = data;
            }
        }
    }
}

[Serializable]
public class ShaderInfo
{
    public string m_ShaderName;
    public string m_ShaderType;
    public List<string> m_GlobalKeywords = new List<string>();
    public List<string> m_LocalKeywords = new List<string>();

    public string m_GlobalKeywordsText;
    public string m_LocalKeywordsText;

    public ShaderBaseInfo m_DefaultInfo = null;
    public ShaderBaseInfo m_PositionVariant = null;
    public ShaderBaseInfo m_VaringVariant = null;

    public ShaderProperties m_ShaderProperties = null;

    public bool Expand { set; get; } = true;
}

[Serializable]
public class ShaderBaseInfo
{
    public int m_WorkRegisters;
    public int m_UniformRegisters;
    public bool m_StackSpilling;
    public float m_Arithmetic16Bit;
    public ShaderCycles m_TotalShaderCycles = new ShaderCycles();
    public ShaderCycles m_ShortestShaderCycles = new ShaderCycles();
    public ShaderCycles m_LongestShaderCycles = new ShaderCycles();

    public class ShaderCycles
    {
        public float m_A;
        public float m_LS;
        public float m_V;
        public float m_T;
        public string m_Bound;
    }
}

public class ShaderProperties
{
    public bool m_HasUniformComputation = false;
    public bool m_HasSideEffects = false;
    public bool m_ModifiesCoverage = false;
    public bool m_UsesLateZSTest = false;
    public bool m_UseLateZSUpdate = false;
    public bool m_ReadColorBuffer = false;
}